//  Copyright (c) 2008  Yoong-ki Ahn & Youngjin Kim < mexif@googlegroups.com>
//  See the enclosed file COPYING for license information (MIT Licence).
//  If you did not receive this file,
//  see http://www.opensource.org/licenses/mit-license.php.

package mexif;

import mexif.TagDB.*;

class EnumMakers {
	final static public int MEXIF_MAKER_UNKNOWN		= 0;
	final static public int MEXIF_MAKER_AGFA		= 1;
	final static public int MEXIF_MAKER_CANON		= 2;
	final static public int MEXIF_MAKER_CASIO		= 3;
	final static public int MEXIF_MAKER_EPSON		= 4;
	final static public int MEXIF_MAKER_FUJIFILM	= 5;
	final static public int MEXIF_MAKER_KODAK		= 6;
	final static public int MEXIF_MAKER_KONICA		= 7;
	final static public int MEXIF_MAKER_MINOLTA		= 8;
	final static public int MEXIF_MAKER_KYOCERA		= 9;
//	final static public int MEXIF_MAKER_CONTAX,
	final static public int MEXIF_MAKER_NIKON		= 10;
	final static public int MEXIF_MAKER_OLYMPUS		= 11;
	final static public int MEXIF_MAKER_PANASONIC	= 12;
	final static public int MEXIF_MAKER_PENTAX		= 13;
	final static public int MEXIF_MAKER_POLAROID	= 14;
//	final static public int MEXIF_MAKER_ASAHI,
	final static public int MEXIF_MAKER_RICOH		= 15;
	final static public int MEXIF_MAKER_SAMSUNG		= 16;
	final static public int MEXIF_MAKER_SANYO		= 17;
	final static public int MEXIF_MAKER_SIGMA		= 18;
	final static public int MEXIF_MAKER_SHARP		= 19;
	final static public int MEXIF_MAKER_SONY		= 20;		
	final static public int MEXIF_MAKER_TOSHIBA		= 21;
	final static public int MEXIF_MAKER_COUNT		= 22;
};

// some bad model needs specialized code.
enum BadModels {
	// normal model.
	MEXIF_MODEL_NORMAL,
	MEXIF_MODEL_PANASONIC_DMC_LC5,	///< makernote endian is big endian.
	MEXIF_MODEL_NIKON_COOLPIX2100,	///< fucking nikon. BUG: PreviewImage offset.
	MEXIF_MODEL_NIKON_COOLPIX3100,  ///< nikon sucks.   BUG: PreviewImage offset.
	MEXIF_MODEL_RICOH_RR120,		///< unknown makernote tag.
	MEXIF_MODEL_RICOH_RDC7S,		///< unknown makernote tag.
	MEXIF_MODEL_MINOLTA_5D,
	MEXIF_MODEL_MINOLTA_7D,
	MEXIF_MODEL_SONY_A100,
	MEXIF_MODEL_SONY_A700
};

/// well-known makernote type.
enum MakerNotes {
	MEXIF_MAKERNOTE_UNKNOWN,
	MEXIF_MAKERNOTE_CANON,
	MEXIF_MAKERNOTE_CASIO1,
	MEXIF_MAKERNOTE_CASIO2,
	MEXIF_MAKERNOTE_FUJIFILM,
	MEXIF_MAKERNOTE_PANASONIC1,
	MEXIF_MAKERNOTE_NIKON1,
	MEXIF_MAKERNOTE_NIKON2,
	MEXIF_MAKERNOTE_PENTAX1,
	MEXIF_MAKERNOTE_PENTAX2,
	MEXIF_MAKERNOTE_RICOH,
	MEXIF_MAKERNOTE_SANYO,
	MEXIF_MAKERNOTE_SIGMA,
	MEXIF_MAKERNOTE_SONY,

	MEXIF_MAKERNOTE_AGFA,
	MEXIF_MAKERNOTE_EPSON,
	MEXIF_MAKERNOTE_KONICA,
	MEXIF_MAKERNOTE_MINOLTA,
	MEXIF_MAKERNOTE_KYOCERA,
	MEXIF_MAKERNOTE_CONTAX,
	MEXIF_MAKERNOTE_OLYMPUS,
	MEXIF_MAKERNOTE_ASAHI,
	MEXIF_MAKERNOTE_SAMSUNG
};

//#define MEXIF_MAKE_FORMAT( base, index )	((base-1)<<24) | (index)
//#define MEXIF_GET_BASE_FORMAT( fmt )		(((fmt>>24)&0xff) +1)

class EnumSectionFormat {
	final static public int MEXIF_BASE_FORMAT_IFD		= 1;
	final static public int MEXIF_BASE_FORMAT_U8ARRAY	= 2;
	final static public int MEXIF_BASE_FORMAT_U16ARRAY	= 3;
	final static public int MEXIF_BASE_FORMAT_U32ARRAY	= 4;
	
	static int makeFormat(int base, int index ) {
		return ((base-1) << 24) | (index);
	}
	
	final static public int MEXIF_FORMAT_UNKNOWN	= 0;
	final static public int MEXIF_FORMAT_EXIF_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 1 );
	final static public int MEXIF_FORMAT_GPS_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 2 );
	final static public int MEXIF_FORMAT_INTEROP_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 3 );
	final static public int MEXIF_FORMAT_CANON_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 4 );
	final static public int MEXIF_FORMAT_CASIO1_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 5 );
	final static public int MEXIF_FORMAT_CASIO2_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 6 );
	final static public int MEXIF_FORMAT_FUJIFILM_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 7 );
	final static public int MEXIF_FORMAT_KONICA_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 8 );
	final static public int MEXIF_FORMAT_MINOLTA_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 9 );
	final static public int MEXIF_FORMAT_NIKON1_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x0a );
	final static public int MEXIF_FORMAT_NIKON2_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x0b );
	final static public int MEXIF_FORMAT_OLYMPUS_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x0c );
	final static public int MEXIF_FORMAT_PANASONIC1_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x0d );
	final static public int MEXIF_FORMAT_PENTAX1_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x0e );
	final static public int MEXIF_FORMAT_PENTAX2_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x0f );
	final static public int MEXIF_FORMAT_POLAROID_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x10 );
	final static public int MEXIF_FORMAT_RICOH_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x11 );
	final static public int MEXIF_FORMAT_SAMSUNG_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x12 );
	final static public int MEXIF_FORMAT_SANYO_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x13 );
	final static public int MEXIF_FORMAT_SHARP_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x14 );
	final static public int MEXIF_FORMAT_SIGMA_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x15 );
	final static public int MEXIF_FORMAT_SONY_IFD				= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x16 );
	final static public int MEXIF_FORMAT_TOSHIBA_IFD			= makeFormat( MEXIF_BASE_FORMAT_IFD, 0x17 );

	final static public int MEXIF_FORMAT_CANON_CameraSettings	= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0400 );
	final static public int MEXIF_FORMAT_CANON_FocalLength		= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0401 );
	final static public int MEXIF_FORMAT_CANON_ShotInfo			= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0402 );
	final static public int MEXIF_FORMAT_CANON_Panorama			= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0403 );
	final static public int MEXIF_FORMAT_CANON_AFInfo			= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0404 );
	final static public int MEXIF_FORMAT_CANON_MyColors			= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0405 );
	final static public int MEXIF_FORMAT_CANON_FaceDetect1		= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0406 );
	final static public int MEXIF_FORMAT_CANON_FaceDetect2		= makeFormat( MEXIF_BASE_FORMAT_U8ARRAY,  0x0407 );
	final static public int MEXIF_FORMAT_CANON_AFInfo2			= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0408 );
	final static public int MEXIF_FORMAT_CANON_FileInfo			= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0409 );
	final static public int MEXIF_FORMAT_CANON_ProcessingInfo	= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x040a );
	final static public int MEXIF_FORMAT_CANON_SensorInfo		= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x040b );
	final static public int MEXIF_FORMAT_CANON_ColorInfo		= makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x040c );

	final static public int MEXIF_FORMAT_MINOLTA_CameraSettings     = makeFormat( MEXIF_BASE_FORMAT_U32ARRAY, 0x0901 );
	final static public int MEXIF_FORMAT_MINOLTA_CameraSettings7D   = makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0902 );
	final static public int MEXIF_FORMAT_MINOLTA_CameraSettings5D   = makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0903 );
	final static public int MEXIF_FORMAT_MINOLTA_CameraSettingsA100 = makeFormat( MEXIF_BASE_FORMAT_U16ARRAY, 0x0904 );

	final static public int MEXIF_FORMAT_NIKON1_PreviewImage	= makeFormat( MEXIF_BASE_FORMAT_IFD,		 0x0a01 );
	final static public int MEXIF_FORMAT_NIKON1_AFInfo			= makeFormat( MEXIF_BASE_FORMAT_U8ARRAY,	 0x0a02 );

	final static public int MEXIF_FORMAT_OLYMPUS_Equipment			= makeFormat( MEXIF_BASE_FORMAT_IFD,		 0x0c01 );
	final static public int MEXIF_FORMAT_OLYMPUS_CameraSettings		= makeFormat( MEXIF_BASE_FORMAT_IFD,		 0x0c02 );
	final static public int MEXIF_FORMAT_OLYMPUS_FocusInfo			= makeFormat( MEXIF_BASE_FORMAT_IFD,		 0x0c03 );

	final static public int MEXIF_FORMAT_PENTAX1_ShakeReductionInfo	= makeFormat( MEXIF_BASE_FORMAT_U8ARRAY,	 0x0e01 );
	final static public int MEXIF_FORMAT_PENTAX1_ShotInfo			= makeFormat( MEXIF_BASE_FORMAT_U8ARRAY,	 0x0e02 );
}

class ExifCore {
	byte[]		mData;
	int			mDataOffset;
	int			mSize;
	
	int			mMaker;
	BadModels	mModel;
	MakerNotes	mMakerNote;
}

class IFDCore {
	int			mSection;
	boolean		mLittleEndian;
	int			mSectionFormat;
	
	int			mDataOffset;
	
	int			mOffset;
	int			mSize;
	
	void copy(final IFDCore src) {
		mSection = src.mSection;
		mLittleEndian = src.mLittleEndian;
		mSectionFormat = src.mSectionFormat;
		mDataOffset = src.mDataOffset;
		
		mOffset = src.mOffset;

		mSize = src.mSize;
	}
}

class ExifTag {
	ExifCore	mExif;
	IFDCore		mIFD;

	ExifTagDB mTagDBEntry;

	//const unsigned char*	value_pointer;
	int			mTagPointerOffset;
	int			mTagID;

	int	mValueFormat;
	int mValuePointerOffset;
	int	mValueType;
	int	mValueCount;
	int	mValueSize;
	int	mValueOffset;
	
	void init() {
		mExif = null;
		mIFD = null;
		mTagDBEntry = null;
		mTagPointerOffset = mTagID = 0;
		mValueFormat = mValueType = mValueCount = mValueSize = mValueOffset = 0;
	}
}

class ExifSection {
	ExifCore	mExif;
	IFDCore		mIFD;
	
	ExifTagDB[] mTagDB;
}

class EnumValueType {
	final static public int MEXIF_VALUE_BYTE		= 1;
	final static public int MEXIF_VALUE_ASCII		= 2;	///< null terminated.
	final static public int MEXIF_VALUE_UINT16		= 3;    ///< uint16
	final static public int MEXIF_VALUE_UINT32		= 4;	///< uint32
	final static public int MEXIF_VALUE_RATIONAL	= 5;	///< uint32 / uint32
	final static public int MEXIF_VALUE_SINT8		= 6;
	final static public int MEXIF_VALUE_UNDEFINED	= 7;	///< byte array.
	final static public int MEXIF_VALUE_SINT16		= 8;	///< int16
	final static public int MEXIF_VALUE_SINT32		= 9;	///< int32
	final static public int MEXIF_VALUE_SRATIONAL	= 10;	///< int32 / int32
};

public class Exif {
	byte[] mData;
	int mBaseOffset, mExifSize;
	boolean mLittleEndian;
	int mTiffHeaderOffset;
	
	ExifCore mCore = null;
	IFDCore[] mIFDCore = null;
	
	public Exif( byte[] data, int offset, int exifSize) throws JpegException {
		// Initialize Basic Data
		mCore = new ExifCore();
		mIFDCore = new IFDCore[EnumExifSection.MEXIF_SECTION_COUNT];
		for(int i=0; i<EnumExifSection.MEXIF_SECTION_COUNT; i++)
			mIFDCore[i] = new IFDCore();
		
		// Check Exif Tag Information
		if( ExifUtil.isExifMarkerPrefixed(data, offset)) {
			mData = data;
			mBaseOffset = offset + 6;	// "Exif\0\0"
			mExifSize = exifSize - 6;
		}
		else
			throw new JpegException("Not Exif Information");
		
		int currentOffset = mBaseOffset;
		// Determine Bit-Endian
		if( mData[currentOffset] == 'I' && mData[currentOffset+1] == 'I' )
			mLittleEndian = true;
		else if( mData[currentOffset] == 'M' && mData[currentOffset+1] == 'M' )
			mLittleEndian = false;
		else
			throw new JpegException("Unknown Endian Signature");
		currentOffset += 2;
		
		// Check Magic Number
		int magic = ByteUtil.get16u(mData, currentOffset, mLittleEndian);
		if( 0x2a != magic )
			throw new JpegException("Invalid Exif Magic Number");
		currentOffset+=2;
		
		// Get Tiff Header Offset
		mTiffHeaderOffset = (int)ByteUtil.get32u(mData, currentOffset, mLittleEndian);
		
		// initialize first IFD (IFD_0)	
		{
			IFDCore ifdCore = mIFDCore[EnumExifSection.MEXIF_SECTION_0];
			ifdCore.mSection = EnumExifSection.MEXIF_SECTION_0;
			ifdCore.mSectionFormat = EnumSectionFormat.MEXIF_FORMAT_EXIF_IFD;
			ifdCore.mLittleEndian = mLittleEndian;
			ifdCore.mDataOffset = mBaseOffset;
			
			ifdCore.mOffset = mTiffHeaderOffset;
			ifdCore.mSize = mExifSize;
		}
		
		mCore.mData = mData;
		mCore.mDataOffset = mBaseOffset;
		mCore.mSize = mExifSize;
		
		// initialize second IFD (IFD_1)
		{
			ExifSection section = new ExifSection();
			selectSection(section, EnumExifSection.MEXIF_SECTION_0);
			
			final int pc = section.mIFD.mDataOffset + section.mIFD.mOffset;
			int ifd1_offset = (int) ByteUtil.get32u(mData, pc + 2 + section.mTagDB.length*12, section.mIFD.mLittleEndian);
			
			if( ifd1_offset != 0 ) {
				IFDCore ifdCore = mIFDCore[EnumExifSection.MEXIF_SECTION_1];
				// Copy data from IFD0
				ifdCore.copy(mIFDCore[EnumExifSection.MEXIF_SECTION_0]);

				// Change Some values
				ifdCore.mSection = EnumExifSection.MEXIF_SECTION_1;
				ifdCore.mOffset = ifd1_offset;
			}
			
			// @TODO: Implement (get maker)
	// 		if ( MEXIF_RET_OK == mexif_search_tag( &section, &tag, MEXIF_TAGID_EXIF_Make ))
	 //			exif->core.maker  = mexif_which_maker( tag.value_pointer, tag.value_size );
//	 		if ( MEXIF_RET_OK == mexif_search_tag( &section, &tag, MEXIF_TAGID_EXIF_Model ) )
	// 			exif->core.model  = mexif_which_model(  exif->core.maker, tag.value_pointer, tag.value_size );
		}
		
		// find other sections
		{
			ExifSection section = new ExifSection();
			ExifTag tag = new ExifTag();
			selectSection(section, EnumExifSection.MEXIF_SECTION_0);
			
			if( searchTag(section, tag, ExifMetaTagDB_EXIF.MEXIF_TAGID_EXIF_ExifIFDPointer)) {
				IFDCore core = mIFDCore[EnumExifSection.MEXIF_SECTION_EXIF];
				core.copy(mIFDCore[EnumExifSection.MEXIF_SECTION_0]);
				core.mSectionFormat = EnumSectionFormat.MEXIF_FORMAT_EXIF_IFD;
				core.mSection = EnumExifSection.MEXIF_SECTION_EXIF;
				core.mOffset = (int) ExifUtil.uintTagValue(tag);
			}
			if( searchTag(section, tag, ExifMetaTagDB_EXIF.MEXIF_TAGID_EXIF_GpsIFDPointer)) {
				IFDCore core = mIFDCore[EnumExifSection.MEXIF_SECTION_GPS];
				core.copy(mIFDCore[EnumExifSection.MEXIF_SECTION_0]);
				core.mSectionFormat = EnumSectionFormat.MEXIF_FORMAT_GPS_IFD;
				core.mSection = EnumExifSection.MEXIF_SECTION_GPS;
				core.mOffset = (int) ExifUtil.uintTagValue(tag);
			}
			if( searchTag(section, tag, ExifMetaTagDB_EXIF.MEXIF_TAGID_EXIF_InteropIFDPointer)) {
				IFDCore core = mIFDCore[EnumExifSection.MEXIF_SECTION_INTEROP];
				core.copy(mIFDCore[EnumExifSection.MEXIF_SECTION_0]);
				core.mSectionFormat = EnumSectionFormat.MEXIF_FORMAT_INTEROP_IFD;
				core.mSection = EnumExifSection.MEXIF_SECTION_INTEROP;
				core.mOffset = (int) ExifUtil.uintTagValue(tag);
			}
		}
	}
	
	boolean selectSection( ExifSection section, int section_type ) {
		//	empty. unknown ifd.
		if( mIFDCore[section_type].mSection != section_type )
			return false;
		
		section.mExif = mCore;
		section.mIFD = mIFDCore[section_type];
		
		if( ExifUtil.isIFD( section.mIFD.mSectionFormat ) ) {
			final int count = countIFDTags(section.mIFD);
			// Read Tags later. Now allocate memory only
			section.mTagDB = new ExifTagDB[count];
		}
		
		return true;
	}
	
	
	
	int countIFDTags( IFDCore ifd ){
		return ByteUtil.get16u(mData, ifd.mDataOffset + ifd.mOffset, ifd.mLittleEndian);
	}
	
	int countTags(ExifSection section) { return section.mTagDB.length; }
	
	boolean searchTag( ExifSection section, ExifTag tag, int tagid) {
		int n = countTags(section);
		for(int i=0; i<n; i++) {
			if( selectTag(section, tag, i) == true && tag.mTagID == tagid )
				return true;
		}

		tag.init();
		return false;
	}
	
	boolean selectTag(ExifSection section, ExifTag tag, int index ) {
		if( countTags(section) <= index || mData == null )
			return false;

		tag.mExif = section.mExif;
		tag.mIFD = section.mIFD;
		
		tag.mValueFormat = EnumSectionFormat.MEXIF_FORMAT_UNKNOWN;
	
		int pbOffset = section.mIFD.mDataOffset + section.mIFD.mOffset;
		if( ExifUtil.isIFD( section.mIFD.mSectionFormat )) {
			tag.mTagDBEntry = null;
			
			pbOffset += 2 + index * 12;
			tag.mTagPointerOffset = pbOffset;
			tag.mTagID = ByteUtil.get16u(mData, pbOffset, tag.mIFD.mLittleEndian);
			
			{
				ExifMetaTagDB db = initTagDB(tag.mIFD.mSectionFormat);
				if( db != null )
					tag.mTagDBEntry = db.searchDB( tag.mTagID );
				
				// sony a-700 uses minolta IFD
				if( section.mIFD.mSectionFormat == EnumSectionFormat.MEXIF_FORMAT_SONY_IFD && tag.mExif.mModel == BadModels.MEXIF_MODEL_SONY_A700 ) {
					if( tag.mTagDBEntry == null ) {
						db = initTagDB(EnumSectionFormat.MEXIF_FORMAT_MINOLTA_IFD);
						if( db != null )
							tag.mTagDBEntry = db.searchDB( tag.mTagID );
						// MinoltaCameraSettings5D -> MinoltaCameraSettingsA100
						if( tag.mTagID == ExifMetaTagDB_Minolta.MEXIF_TAGID_MINOLTA_MinoltaCameraSettings5D )
							tag.mTagDBEntry = db.searchDB( ExifMetaTagDB_Minolta.MEXIF_TAGID_MINOLTA_MinoltaCameraSettingsA100 );
					}
				}
			}
			tag.mValueType = ByteUtil.get16u(mData, pbOffset+2, tag.mIFD.mLittleEndian);
			tag.mValueCount = (int)ByteUtil.get32u(mData, pbOffset+4, tag.mIFD.mLittleEndian);
			
			switch( tag.mValueType )
			{
			case EnumValueType.MEXIF_VALUE_UINT16 :
			case EnumValueType.MEXIF_VALUE_SINT16 :
				tag.mValueSize = 2 * tag.mValueCount;
				break;
			case EnumValueType.MEXIF_VALUE_UINT32 :
			case EnumValueType.MEXIF_VALUE_SINT32 :
				tag.mValueSize = 4 * tag.mValueCount;
				break;
			case EnumValueType.MEXIF_VALUE_SRATIONAL :
			case EnumValueType.MEXIF_VALUE_RATIONAL :
				tag.mValueSize = 8 * tag.mValueCount;
				break;
			case EnumValueType.MEXIF_VALUE_SINT8 :
			case EnumValueType.MEXIF_VALUE_ASCII :
			case EnumValueType.MEXIF_VALUE_BYTE :
			case EnumValueType.MEXIF_VALUE_UNDEFINED :
				tag.mValueSize = tag.mValueCount;
				break;
			default :
				tag.mValueSize = tag.mValueCount;
				//assert( 0 );
				return false;
			}
			
			if ( tag.mValueSize <= 4 )
			{
				tag.mValuePointerOffset = pbOffset + 8;
				tag.mValueOffset = (pbOffset - section.mIFD.mDataOffset) + 8; // Because we add 8 more
			}
			else
			{
				tag.mValueOffset = (int) ByteUtil.get32u(mData, pbOffset+8, tag.mIFD.mLittleEndian);
				tag.mValuePointerOffset = section.mIFD.mDataOffset + tag.mValueOffset;	 // Because we add 8 more

				//	PENTAX type 2
				if( tag.mIFD.mSectionFormat == EnumSectionFormat.MEXIF_FORMAT_PENTAX2_IFD ) {
					tag.mValuePointerOffset = pbOffset + tag.mValueOffset;
				}
			}
			/// recognize subsection format.
			tag.mValueFormat = getSubsectionFormat(section, tag);

			// Data end should be valid
			if( tag.mValuePointerOffset + tag.mValueSize <= tag.mExif.mSize )
				return true;
			else
				return false;
		}
		else if ( ExifUtil.isU8Array(section.mIFD.mSectionFormat) ) {
			tag.mTagDBEntry = null;

			tag.mTagPointerOffset = 0;
			tag.mTagID = index;

			{
				ExifMetaTagDB db = initTagDB(section.mIFD.mSectionFormat);
				if( db != null )
					tag.mTagDBEntry = db.searchDB( tag.mTagID );

			}

			tag.mValueType = EnumValueType.MEXIF_VALUE_BYTE;
			tag.mValueCount = 1;
			tag.mValueSize = 1;
			tag.mValuePointerOffset = index;
			tag.mValueOffset = pbOffset + index * 2;

			return true;
		}
		else if ( ExifUtil.isU16Array( section.mIFD.mSectionFormat) ) {
			tag.mTagDBEntry = null;

//			pb += index * 2;
			tag.mTagPointerOffset = 0;
			tag.mTagID = index;

			{
				ExifMetaTagDB db = initTagDB ( section.mIFD.mSectionFormat );
				if( db != null )
					tag.mTagDBEntry = db.searchDB( tag.mTagID );
			}

			tag.mValueType = EnumValueType.MEXIF_VALUE_UINT16;
			tag.mValueCount = 1;
			tag.mValueSize = 2;
			tag.mValuePointerOffset = pbOffset + index*2;
			tag.mValueOffset = index * 2;


			//	@TODO: Exception for special cases
//			if ( section->ifd.format == MEXIF_FORMAT_MINOLTA_CameraSettings7D )
//			{
//				if ( index == MEXIF_TAGID_MINOLTA_CameraSettings7D_ExposureCompensation )
//					tag->value_type    = MEXIF_VALUE_SINT16;
//			}
//			else if ( section->ifd.format == MEXIF_FORMAT_MINOLTA_CameraSettings5D )
//			{
//				if ( index == MEXIF_TAGID_MINOLTA_CameraSettings5D_ColorTemperature )
//					tag->value_type    = MEXIF_VALUE_SINT16;
//				else if ( index == MEXIF_TAGID_MINOLTA_CameraSettings5D_ColorTemperature2 )
//					tag->value_type    = MEXIF_VALUE_SINT16;
//			}

			return true;
		}
		else if ( ExifUtil.isU32Array( section.mIFD.mSectionFormat ) ) {
			tag.mTagDBEntry = null;

			tag.mTagPointerOffset = 0;
			tag.mTagID = index;

			{
				ExifMetaTagDB db = initTagDB ( section.mIFD.mSectionFormat );
				if( db != null )
					tag.mTagDBEntry = db.searchDB ( tag.mTagID );
			}

			tag.mValueType = EnumValueType.MEXIF_VALUE_UINT32;
			tag.mValueCount = 1;
			tag.mValueSize = 4;
			tag.mValuePointerOffset = pbOffset + index * 4;
			tag.mValueOffset = index * 4;

			return true;
		}
		return false;
	}
	
	ExifMetaTagDB initTagDB( int format ) {
		if( format == EnumSectionFormat.MEXIF_FORMAT_EXIF_IFD ) return new ExifMetaTagDB_EXIF();
		if( format == EnumSectionFormat.MEXIF_FORMAT_GPS_IFD) return new ExifMetaTagDB_GPS();
		if( format == EnumSectionFormat.MEXIF_FORMAT_INTEROP_IFD) return new ExifMetaTagDB_INTEROP();
		
		return null;
	}

	// @TODO: Sub-Section is not implemented yet.
	int getSubsectionFormat( ExifSection section, ExifTag tag) {
//		int cur = section.mIFD.mSectionFormat;
//		BadModels mod = section.mExif.mModel;
//		MakerNotes mak = section.mExif.mMakerNote;
//		
//		if ( cur == EnumSectionFormat.MEXIF_FORMAT_CANON_IFD ) {
//			switch(tag.mTagID ){
//			
//			}
//		}
		
		return EnumSectionFormat.MEXIF_FORMAT_UNKNOWN;
			
	}
	
	TagValue[] getExifSection(int section_type) {
		ExifSection section = new ExifSection();
		if( this.selectSection(section, section_type) == false )
			return new TagValue[0];
		
		// @TODO: if IFD_1, show thumbnail image
		if ( section_type == EnumExifSection.MEXIF_SECTION_1 ) {
			
		}
		
		ExifTag tag = new ExifTag();
		TagValue[] tags = new TagValue[section.mTagDB.length];
		for(int i=0; i<section.mTagDB.length; i++) {
			// Select Tag Error
			if( selectTag(section, tag, i) == false ) {
				continue;
			}
			TagValue tagValue = new TagValue();
			tagValue.mTagName = ExifUtil.formatTagName(tag);
			tagValue.mTagDesc = ExifUtil.formatTagDesc(tag);
			tagValue.mTagValue = ExifUtil.formatTagValue(tag);
			tags[i] = tagValue;
			
			if( tag.mValueFormat == 0 )
				continue;
		}
		return tags;
	}
	
	boolean printExifSection(int section_type ) {
		ExifSection section = new ExifSection();
		if( this.selectSection(section, section_type) == false )
			return false;
		
		System.out.println("Exif: " + ExifUtil.getSectionTypeName( section_type ));
		System.out.println("Total " + section.mTagDB.length + " tags in this section.");
		
		// @TODO: if IFD_1, show thumbnail image
		if ( section_type == EnumExifSection.MEXIF_SECTION_1 ) {
			
		}
		
		ExifTag tag = new ExifTag();
		for(int i=0; i<section.mTagDB.length; i++) {
			
			// Select Tag Error
			if( selectTag(section, tag, i) == false ) {
				System.out.println( "Load Error: " + i + " th tag");
				continue;
			}
			
			String strTagName = ExifUtil.formatTagName(tag);
			String strTagDesc = ExifUtil.formatTagDesc(tag);
			String strTagValue = ExifUtil.formatTagValue(tag);
			System.out.println("Tag Name: " + strTagName + "(" + strTagDesc + ") = " + strTagValue);
			
			if( tag.mValueFormat == 0 )
				continue;
		}
		
		return true;
	}
	
	protected void parseTiffHeader() {
		
	}
}
